/*
 * view.vvsfs - print a summary of the data in the entire file system
 *
 * Eric McCreath 2006 GPL
 * To compile :
 *   gcc view.vvsfs.c -o view.vvsfs
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

#include "vvsfs.h"

char* device_name;
int device, block_size;
struct vvsfs_block *block=NULL;
struct vvsfs_device_block *device_block=NULL;

static void die(char *mess) {
    fprintf(stderr,"Exit : %s\n",mess);
    exit(1);
}

static void usage(void) {
    die("Usage : view.vvsfs <device name>");
}

static inline void readblock(off_t pos) {
    if (pos != lseek(device,pos,SEEK_SET)){
        free(block);
        free(device_block);
        die("seek set failed");
    }
    if (block_size != read(device,block,block_size)){
        free(block);
        free(device_block);
        die("inode read failed");
    }
}

int main(int argc, char ** argv) {
    if (argc != 2) usage();

    // open the device for reading
    device_name = argv[1];
    device = open(device_name,O_RDONLY);

    //Generate the device info.
    int device_block_size=sizeof(struct vvsfs_device_block);
    device_block=(struct vvsfs_device_block *)malloc(device_block_size);
    //Read the device information.
    if(device_block_size != read(device, device_block, device_block_size)) {
        free(device_block);
        die("Read device info error.");
    }
    //Read the device info.
    block_size=device_block->block_size;
    //Set the pos.
    off_t pos=0;
    block=(struct vvsfs_block *)malloc(block_size);
    if(block==NULL) {
        free(device_block);
        die("No enough memory.");
    }
    // print header images.
    printf(HEADER_IMAGES, "Raw File Viewer", VER_MAJOR, VER_MINOR, VER_PATCH, VER_SPECIAL);
    printf("Device information\n"
           "Block size           : %d bytes\n"
           "Total blocks         : %d blocks\n"
           "Inode blocks         : %d blocks\n"
           "Last used block      : 0x%08x\n",
           device_block->block_size,
           device_block->block_count,
           device_block->inode_block_count,
           device_block->last_used_block);
    //Calculate the root block start position.
    int i, j, block_count=device_block->block_count,
            root_block_index=device_block->inode_block_count+BLOCK_INODE_LIST;
    printf("Root Dir Block Index : 0x%08x\n"
           "Data blocks          : %d blocks\n",
           root_block_index,
           block_count-root_block_index+1);
    int inode_max_count=block_size/sizeof(struct vvsfs_inode),
            total_inodes=inode_max_count*(device_block->inode_block_count);
    printf("Max inode per block  : %d inodes\n"
           "Total inodes         : %d inodes\n",
           inode_max_count, total_inodes);
    //Read journal data.
    readblock(block_size*BLOCK_JOURNAL_STATE);
    printf("Journal state        : ");
    struct vvsfs_journal_state *journal_state=
            (struct vvsfs_journal_state *)block;
    switch(journal_state->state) {
    case JOURNAL_NONE:
        printf("None\n");
        break;
    case JOURNAL_LOGGING:
        printf("Writing journal\n");
        break;
    case JOURNAL_WRITING:
        printf("Writing data\n");
        break;
    default:
        printf("Unknown\n");
        break;
    }
    printf("Journaled inode      : %d\n", journal_state->inode_number);
    printf("Journaled operation  : ");
    switch(journal_state->file_operation) {
    case JOURNAL_OP_NONE:
        printf("None\n");
        break;
    case JOURNAL_OP_WRITEFILE:
        printf("File Write\n");
        break;
    case JOURNAL_OP_SETATTR:
        printf("Set Attr\n");
        break;
    default:
        printf("Unknown\n");
        break;
    }
    printf("\n");
    pos=block_size*BLOCK_INODE_LIST;
    int used_inodes=0, inode_id=0;
    struct vvsfs_inode *inode;
    printf("Inode Area\n"
           "┌────────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┐\n"
           "│   ID   │ State │ Count │  Size  │ C Size │  User  │  Group │  Mode  │Block no│\n"
           "├────────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┤\n");
    for(i=BLOCK_INODE_LIST; i<root_block_index; ++i) {
        //Read the block information.
        readblock(pos);
        inode=(struct vvsfs_inode *)block;
        //Loop to find all the inodes.
        for(j=0; j<inode_max_count; ++j) {
            if(inode->flag & VVSFS_IFLAG_ISUSED) {
                printf("│%08d│%s      │%7d│%8d│%8d│%8x│%8x│%8x│%08x│\n",
                       inode_id,
                       (inode->flag & VVSFS_IFLAG_COMPRESS)?"C":" ",
                       inode->count,
                       inode->size,
                       inode->c_size,
                       inode->uid,
                       inode->gid,
                       inode->mode,
                       inode->block);
                ++used_inodes;
            }
            ++inode_id;
            ++inode;
        }
        pos+=block_size;
    }
    printf("└────────┴───────┴───────┴────────┴────────┴────────┴────────┴────────┴────────┘\n"
           "Used inodes          : %d inodes\n"
           "Free inodes          : %d inodes\n\n",
           used_inodes, total_inodes-used_inodes);

    //Initial the position.
    pos=block_size*root_block_index;
    printf("Data Area\n"
           "┌────────┬───────┬────────┬─────┬──────┐\n"
           "│   ID   │ State │  Hash  │Count│ Size │ Data\n"
           "├────────┼───────┼────────┼─────┼──────┤\n");
    // read each of the blocks
    for (i = root_block_index; i < block_count; ++i) {
        //Read the block information.
        readblock(pos);
        //Print block data.
        printf("│%08x│%s%s%s    │%08x│%5i│%6i│ ", i,
               ((i == root_block_index)?"R":" "),
               ((block->flag & VVSFS_FLAG_ISEMPTY)?"E":" "),
               ((block->flag & VVSFS_FLAG_ENCRYPT)?"A":" "),
               block->hash,
               block->count,
               block->size);
        int j;
        for (j=0;j< block->size;j++) {
            if (block->data[j] == '\n') {
                printf("\\n ");
            } else if ((uint)block->data[j]<20) {
                printf("%02x ",block->data[j]);
            }
            else {
                printf("%lc ",block->data[j]);
            }
        }
        printf("\n");
        pos += block_size;
    }
    printf("└────────┴───────┴────────┴─────┴──────┘\n");
    free(block);
    free(device_block);
    close(device);
    return 0;
}

